package com.sunny.shop.core.interceptors;

import com.auth0.jwt.interfaces.Claim;
import com.sunny.shop.core.LocalUser;
import com.sunny.shop.exception.httpe.ForbiddenException;
import com.sunny.shop.exception.httpe.UnAuthenticatedException;
import com.sunny.shop.model.User;
import com.sunny.shop.service.UserServiceImpl;
import com.sunny.shop.util.JwtToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.Optional;

@Component
public class PermissionInertceptor extends HandlerInterceptorAdapter {
    @Autowired
    private UserServiceImpl userService;
    public PermissionInertceptor() {
        super();
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Optional<ScopeLevel> scoperLeval = this.getScoperLeval(handler);
        if(!scoperLeval.isPresent()){
            return true;
        }
        String beareToken = request.getHeader("Authorization");
        if(StringUtils.isEmpty(beareToken)){
            throw new UnAuthenticatedException(10005);
        }
        if(!beareToken.startsWith("Bearer")){
            throw new UnAuthenticatedException(10005);
        }
        String[] tokens = beareToken.split(" ");
        if(!(tokens.length == 2)){
            throw new UnAuthenticatedException(10005);
        }
        String token = tokens[1];
        Optional<Map<String, Claim>> mapOptional = JwtToken.getClain(token);
        Map<String, Claim> map = mapOptional.orElseThrow(() -> new UnAuthenticatedException(10005));
        boolean valid = this.hasPermission(scoperLeval.get(), map);
        if(valid){
            this.setToThreadLocal(map);
        }
        return valid;
    }

    private void setToThreadLocal(Map<String, Claim> map){
        Long uid = map.get("uid").asLong();
        Integer scope = map.get("scope").asInt();
        User user = userService.getUserByUid(uid);
        LocalUser.set(user, scope);
    }

    private boolean hasPermission(ScopeLevel scopeLevel, Map<String, Claim> map){
        Integer value = scopeLevel.value();
        Integer scope = map.get("scope").asInt();
        if(value > scope){
            throw new ForbiddenException(10006);
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        LocalUser.clear();
        super.afterCompletion(request, response, handler, ex);
    }

    private Optional<ScopeLevel> getScoperLeval(Object handler){
        if(handler instanceof HandlerMethod){
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            ScopeLevel annotation = handlerMethod.getMethod().getAnnotation(ScopeLevel.class);
            if(annotation == null){
                return Optional.empty();
            }
            return Optional.of(annotation);
        }
        return Optional.empty();

    }
}
